package main

import (
	"io/ioutil"
	"encoding/json"
	"os"
	"crypto/sha1"
	"encoding/hex"
	"io"
	"time"
	"strings"
	"fmt"
)

//自动修改指定目录下的所有文件
// 归类文件，根据年月归类
// 识别文件创建时间，按照创建时间重新命名文件名称，并加入sha1后缀部分

//主运行程序
func main(){
	//检查该程序所在目录下所有文件
	list,err := GetFileList(".",[]string{"mp4","MP4","jpg","JPG","mov","MOV","AAE","aae"},true)
	if err != nil{
		fmt.Println(err.Error())
	}
	//遍历所有文件，忽略文件夹
	for _,v := range list{
		if IsFolder(v) == true{
			continue
		}
		info,err := GetFileInfo(v)
		if err != nil{
			fmt.Println(err.Error())
			continue
		}
		times := strings.Split(info.ModTime().String()," ")
		times2 := strings.Split(times[0],"-")
		times3 := strings.Split(times[1],":")
		dirsrc := "." + Sep + times2[0] + times2[1] + times2[2]
		//确保文件夹存在
		if IsExist(dirsrc) == false{
			err = CreateFolder(dirsrc)
			if err != nil{
				fmt.Println(err.Error())
				continue
			}
		}
		//获取SHA1
		sha,err := GetFileSha1(v)
		if err != nil{
			fmt.Println(err.Error())
			continue
		}
		//获取文件类型
		fileinfos,err := GetFileNames(v)
		if err != nil{
			fmt.Println(err.Error())
			continue
		}
		//建立新的文件路径
		newSrc := dirsrc + Sep + times2[0] + times2[1] + times2[2] + times3[0] + times3[1] + times3[2] + "_" + sha + "." + fileinfos["type"]
		//转移文件
		err = MoveF(v,newSrc)
		if err != nil{
			fmt.Println(err.Error())
			continue
		}
		//输出日志
		fmt.Println("move file : " + v + " , to : " + newSrc)
	}
}

var (
	//系统文件路径分隔符
	Sep string = string(os.PathSeparator )
)

//读取配置文件
//param src string 配置文件路径
//return map[string]interface{},error 配置信息,错误
func LoadConfig(src string) (map[string]interface{}, error) {
	var res map[string]interface{}
	cb,err := ioutil.ReadFile(src)
	if err != nil{
		return res,err
	}
	err = json.Unmarshal(cb, &res)
	if err != nil {
		return res, err
	}
	return res, nil
}

//保存配置文件
//param src string 配置文件路径
//param data map[string]interface{} 配置信息
//return error 错误
func SaveConfig(src string, data map[string]interface{}) error {
	cJson, err := json.Marshal(data)
	if err != nil{
		return err
	}
	return ioutil.WriteFile(src, cJson, os.ModeAppend)
}

//移动文件或文件夹
//param src string 文件路径
//param dest string 新路径
//return error
func MoveF(src string, dest string) error {
	return os.Rename(src, dest)
}

//判断文件或文件夹是否存在
//param src string 文件路径
//return bool 是否存在
func IsExist(src string) bool {
	_, err := os.Stat(src)
	return err == nil && os.IsNotExist(err) == false
}

//创建多级文件夹
//param src string 新文件夹路径
//return error
func CreateFolder(src string) error {
	return os.MkdirAll(src, os.ModePerm)
}

//判断是否为文件夹
//param src string 文件夹路径
//return bool 是否为文件夹
func IsFolder(src string) bool {
	info, err := os.Stat(src)
	return err == nil && info.IsDir()
}

//判断是否为文件
//param src string 文件路径
//return bool 是否为文件
func IsFile(src string) bool {
	info, err := os.Stat(src)
	return err == nil && !info.IsDir()
}

//获取文件信息
//param src string 文件路径
//return os.FileInfo,error 文件信息，错误
func GetFileInfo(src string) (os.FileInfo, error) {
	c, err := os.Stat(src)
	return c, err
}

//获取文件SHA1值
//param src string 文件路径
//return string,error SHA1值,错误
func GetFileSha1(src string) (string,error) {
	content, err := LoadFile(src)
	if err != nil {
		return "",err
	}
	if content != nil {
		sha := sha1.New()
		_, err = sha.Write(content)
		if err != nil {
			return "",err
		}
		res := sha.Sum(nil)
		return hex.EncodeToString(res),nil
	}
	return "",nil
}

//读取文件
//param src string 文件路径
//return []byte,error 文件数据,错误
func LoadFile(src string) ([]byte,error) {
	fd, err := os.Open(src)
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	c, err := ioutil.ReadAll(fd)
	if err != nil {
		return nil, err
	}
	return c, nil
}

//获取文件句柄SHA1值
//param fd io.Reader 文件句柄
//return string,error SHA1值,错误
func GetFileHandleSha1(fd io.Reader) (string,error){
	c, err := ioutil.ReadAll(fd)
	if err != nil {
		return "", err
	}
	return GetFileSha1(string(c))
}

//获取并创建时间序列创建的多级文件夹
//eg : Return and create the path ,"[src]/201611/"
//eg : Return and create the path ,"[src]/201611/2016110102-03[appendFileType]"
//param src string 文件路径
//param appendFileType string 是否末尾追加文件类型，如果指定值，则返回
//return string,error 新时间周期目录，错误
func GetTimeDirSrc(src string, appendFileType string) (string,error) {
	newSrc := src + Sep + time.Now().Format("200601")
	err := CreateFolder(newSrc)
	if err != nil {
		return "",err
	}
	newSrc = newSrc + Sep
	if appendFileType != "" {
		newSrc = newSrc + time.Now().Format("20060102-03") + appendFileType
	}
	return newSrc,nil
}

//获取文件列表
//param src string 查询的文件夹路径
//param filtre []string 仅保留的文件，文件夹除外
//param isSrc bool 返回是否为文件路径
//return []string,error 文件列表,错误
func GetFileList(src string, filters []string, isSrc bool) ([]string, error) {
	var fs []string
	dir, err := ioutil.ReadDir(src)
	if err != nil {
		return nil, err
	}
	for _, v := range dir {
		var appendSrc string
		if isSrc == true {
			appendSrc = src + Sep + v.Name()
		} else {
			appendSrc = v.Name()
		}
		if v.IsDir() == true || len(filters) < 1 {
			fs = append(fs, appendSrc)
			continue
		}
		names := strings.Split(v.Name(), ".")
		if len(names) == 1 {
			fs = append(fs, appendSrc)
			continue
		}
		t := names[len(names)-1]
		for _, filterValue := range filters {
			if t != filterValue {
				continue
			}
			fs = append(fs, appendSrc)
		}
	}
	return fs, nil
}

//获取文件名称分割序列
//param src string 文件路径
//return map[string]string,error 文件名称序列，错误 eg : {"name","abc.jpg","type":"jpg","only-name":"abc"}
func GetFileNames(src string) (map[string]string, error) {
	info, err := os.Stat(src)
	if err != nil {
		return nil, err
	}
	res := map[string]string{
		"name":      info.Name(),
		"type":      "",
		"only-name": info.Name(),
	}
	names := strings.Split(res["name"], ".")
	if len(names) < 2 {
		return res, nil
	}
	res["type"] = names[len(names)-1]
	res["only-name"] = names[0]
	for i := range names {
		if i != 0 && i < len(names)-1 {
			res["only-name"] = res["only-name"] + "." + names[i]
		}
	}
	return res, nil
}